﻿using System.Linq.Expressions;

namespace EMall.Query
{
    public static class QueryableSortingExtension
    {
        public static IQueryable<TSource> Sorting<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool isDescending = false)
        {
            return isDescending ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
        }
        public static IQueryable<TSource> Sorting<TSource>(this IQueryable<TSource> source, string? sorting)
        {
            if (!string.IsNullOrWhiteSpace(sorting))
            {
                var sortingItems = sorting.ToSortingItems();
                if (sortingItems != null && sortingItems.Any())
                {
                    return source.Sorting(sortingItems);
                }
            }
            return source;
        }
        public static IQueryable<TSource> Sorting<TSource>(this IQueryable<TSource> source, IList<ISortingItem> sortingItems)
        {
            if (sortingItems != null && sortingItems.Any())
            {
                foreach (var item in sortingItems)
                {
                    source = source.Sorting(item.PropertyName, item.IsDescending, sortingItems.IndexOf(item) != 0);
                }
            }
            return source;
        }
        public static IQueryable<TSource> Sorting<TSource>(this IQueryable<TSource> source, string propertyName, bool isDescending = false, bool isThenBy = false)
        {
            if (!string.IsNullOrWhiteSpace(propertyName))
            {
                var propertyList = typeof(TSource).GetProperties();
                var property = propertyList.FirstOrDefault(o => o.Name.ToLower() == propertyName.ToLower());
                if (property != null)
                {
                    var byType = $"{(isThenBy ? "Then" : "Order")}By{(isDescending ? "Descending" : "")}";
                    var param = Expression.Parameter(typeof(TSource), propertyName);
                    var types = new Type[] { typeof(TSource), property.PropertyType };
                    var expr = Expression.Call(typeof(Queryable), byType, types, source.Expression, Expression.Lambda(Expression.Property(param, propertyName), param));
                    return source.Provider.CreateQuery<TSource>(expr);
                }
            }
            return source;
        }
        public static IList<ISortingItem> ToSortingItems(this string sorting, string separator = ",")
        {
            if (!string.IsNullOrWhiteSpace(sorting))
            {
                var sortingArray = sorting.Split(separator);
                if (sortingArray != null)
                {
                    return sortingArray.Where(o => !string.IsNullOrWhiteSpace(o)).Select(sortItem => sortItem.ToSortingItem()).ToList();
                }
            }
            return null;
        }
        public static ISortingItem ToSortingItem(this string sorting, string separator = " ")
        {
            if (!string.IsNullOrWhiteSpace(sorting))
            {
                var sortingArray = sorting.Split(" ");
                if (sortingArray != null)
                {
                    var isDescending = sortingArray.Length == 2 && (
                        sortingArray[1].ToLower() == "descending" ||
                        sortingArray[1].ToLower() == "desc");
                    return new SortingItem(sortingArray[0], isDescending);
                }
            }
            return null;
        }
    }
}
